#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;

const int size = 1024;
//全局的命令行切割存放参数
char* argv[size];
int argc = 0;

//全局的变量-来自定义错误码
int add = 0;


//全局的shell路径
char pwd[size];
char pwdenv[size];
//我的环境变量表
char *genv[size];

//重定向
#define  noneredir   0 //没有重定向-设置为0
#define  inputredir  1 //输入重定向
#define  outputredir 2 //输出重定向
#define  appredir    3 //追加重定向

int redir = 0;//标志位-设置为0表示没有重定向
char* filename = nullptr;//指向对应的重定向文件名

string getuser()//获取用户名
{
    string user = getenv("USER");
    return user.empty() ? "null" : user;//获取失败返回null,成功返回user
}
string gethostname()//获取主机名
{
    string host = getenv("HOSTNAME");
    return host.empty() ? "null" : host;
}
string getpwd()//获取当前路径
{
    if(getcwd(pwd,sizeof(pwd)) == nullptr) return "null";//获取当前路径，获取失败返回null
    //更新环境变量
    // 将 PWD= 和当前工作目录的路径拼接成一个字符串
    snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);
    //所以putenv函数，将更新后的 PWD 环境变量设置到系统中
    putenv(pwdenv);
    return pwd;
}
//处理长路径
string dir()
{
    //获取路径
    string curr = getpwd();
    //判断是否等于根目录或None
    if(curr == "/" || curr == "None") return curr;
    //从后面开始查找 /
    size_t pos = curr.rfind("/");
    //等于npos说明没有找到
    if(pos ==std::string::npos) return curr;
    //substr从posw+1的位置，获取子字符串
    return curr.substr(pos+1);
}


void printfml()//1.打印命令行
{
    printf("[%s@%s %s]$ ",getuser().c_str(),gethostname().c_str(),dir().c_str());
    fflush(stdout);
}

bool getyhml(char getyhml[],int size)//2.获取用户输入的命令
{
    //用fgets可以把空格读取到
    char* ret = fgets(getyhml,size,stdin);//标准输入，获取字符串，放到数组里
    // 如果读取失败（例如遇到文件结束符），返回 false
    if(!ret)
    {
        return false;
    }
    //去掉换行
    getyhml[strlen(getyhml)-1] = 0;
    //输入的命令为空，返回false
    if(strlen(getyhml) == 0) return false;
    return true;
}

bool fxml(char getyhml[],int len)
{
    //将argv里全部置为0
    memset(argv,0,sizeof(argv));
    //个数置为0
    argc = 0;
    // 首次调用 strtok 函数，提取命令
    argv[argc++] = strtok(getyhml," ");
     // 继续调用 strtok 函数，提取后续的参数
    while(argv[argc++] = strtok(nullptr," "));
    argc--;
    return true;
}
//执行命令
bool exeargv()
{
    pid_t id = fork();
    if(id < 0) return  false;
    if(id == 0)
    {
        //子进程
        //执行命令
        execvpe(argv[0],argv,genv);
        //退出
        exit(1);
    }
    int status=0;
    pid_t rid = waitpid(id,&status,0);
    if(rid > 0)
    {
        if(WIFEXITED(status))//子进程正常结束
        {
            // 获取子进程的退出状态码
            add = WEXITSTATUS(status);
        }
        else //代码没跑完-子进程异常结束
        {
            add = 100;
        }
        return true;
    }
    return false;
}
//导入环境变量
void envexport(const char* env)
{
    int i=0;
    // 找到环境变量表的末尾
    while(genv[i])
    {
        i++;
    }
    // 为新的环境变量分配内存
    genv[i] = (char*)malloc(strlen(env)+1);
    //将env字符串，添加到自定义环境变量
    strncpy(genv[i],env,strlen(env));
    // 在环境变量表末尾添加nullptr
    genv[++i] = nullptr;
}


bool njml()
{
    if(strcmp(argv[0],"cd") == 0)
    {
        //个数要等于2，不然只有一个cd，没办法修改路径
        if(argc == 2)
        {
            //调用chdir函数修改路径
            chdir(argv[1]);
            add = 0;//执行成功为0
        }
        else 
        {
            add = 1;
        }
        return true;
    }
    else if(strcmp(argv[0],"export")==0)
    {
        if(argc == 2)
        {
            // 导入环境变量
            envexport(argv[1]);
            add = 0;
        }
        else 
        {
            add = 2;
        }
        return true;
    }
    else if(strcmp(argv[0],"env") == 0)
    {
        int i=0;
        // 遍历自定义环境变量表并打印
        for(i;genv[i];i++)
        {
            printf("[%d]:%s \n",i,genv[i]);
        }
        add = 0;
        return true;
    }
    else if(strcmp(argv[0],"echo") == 0)
    {
        if(argc == 2)
        {
            //echo $?
            //echo hello
            if(argv[1][0] == '$')
            {
                 if(argv[1][1] == '?')
                 {
                     //说明用户查看的是最近一次的退出码
                     printf("%d \n",add);
                     add = 0;//查完了清0
                 }
            }
            else //echo hello 
            {
                printf("%s\n",argv[1]);
                add = 0;
            }
        }
        else 
        {
            add = 3;
        }
        return true;
    }
    return false;
}

// 初始化自定义环境变量表
void hjbl()
{
    extern char** environ;
    int i=0;
    // 复制系统环境变量到自定义环境变量表
    while(environ[i])
    {
        genv[i] = (char*)malloc(strlen(environ[i])+1);
        strncpy(genv[i],environ[i],strlen(environ[i])+1);
        i++;
    }
    // 在环境变量表末尾添加空指针
    genv[i] = nullptr;
}

int main()
{
    //初始化自定义环境变量表
    hjbl();
    char getyhml_add[size];//存放用户命令
    while(true)
    {
        //1.打印命令行
        printfml();
        //2.获取用户输入的命令
        if(!getyhml(getyhml_add,size))
        {
            continue;//重新获取
        }
        printf("%s\n",getyhml_add);
        //重定向
        //"hello bit" > log.txt 
        //"hello bit" < log.txt 
        //"hello bit" >> log.txt


        //3.分析命令
        fxml(getyhml_add,strlen(getyhml_add));
        if(njml())
        {
            continue;
        }


        //4.执行命令
        exeargv();
    }

    return 0;
}

